package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.util.PageUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.*;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexSearchVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl implements SearchService {
	@Autowired
	private AlbumInfoFeignClient albumInfoFeignClient;

	@Autowired
	private CategoryFeignClient categoryFeignClient;

	@Autowired
	private AlbumIndexRepository albumIndexRepository;

	@Autowired
	private UserInfoFeignClient userInfoFeignClient;

	@Autowired
	private ElasticsearchClient elasticsearchClient;

	@Autowired
	private SuggestIndexRepository suggestIndexRepository;

	@Autowired
	private StringRedisTemplate redisTemplate;

	@Autowired
	private RedissonClient redissonClient;

	/**
	 * 上架专辑
	 *
	 * @param albumId
	 */
	@SneakyThrows
	@Override
	public void upperAlbum(Long albumId) {
		//  CompletableFuture 异步编排
		// 	将展示的数据和参与检索的数据放入 ES
		AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
		CompletableFuture<AlbumInfo> cf1 = CompletableFuture.supplyAsync(() -> {
			// 	查询 专辑 的基础数据
			Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
			AlbumInfo albumInfo = albumInfoResult.getData();
			BeanUtils.copyProperties(albumInfo, albumInfoIndex);
			albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));
			return albumInfo;
		});

		CompletableFuture<Void> cf2 = cf1.thenAcceptAsync(albumInfo -> {
			// 属性值
			List<AttributeValueIndex> attributeValueIndexList = albumInfo
					.getAlbumAttributeValueVoList()
					.stream()
					// 将 albumAttributeValue 对象
					.map(albumAttributeValue -> AttributeValueIndex
							.builder()
							.attributeId(albumAttributeValue.getAttributeId())
							.valueId(albumAttributeValue.getValueId())
							.build())
					.collect(Collectors.toList());
			albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
		});

		CompletableFuture<Void> cf5 = cf1.thenAcceptAsync(albumInfo -> {
			// 作者姓名
			Result<UserInfo> userInfoResult = userInfoFeignClient.findUserById(albumInfo.getUserId());
			UserInfo userInfo = userInfoResult.getData();
			albumInfoIndex.setAnnouncerName(userInfo.getNickname());
		});


		CompletableFuture<Void> cf3 = cf1.thenAcceptAsync(albumInfo -> {
			// 分类
			Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.findBaseCategoryView(albumInfo.getCategory3Id());
			BaseCategoryView baseCategoryView = baseCategoryViewResult.getData();
			albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
			albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
			albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
		});


		CompletableFuture<Void> cf4 = CompletableFuture.runAsync(() -> {
			// 统计数据
			Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.getAlbumStatByAlbumId(albumId);
			AlbumStatVo albumStatVo = albumStatVoResult.getData();
			albumInfoIndex.setPlayStatNum(albumStatVo.getPlayStatNum());
			albumInfoIndex.setSubscribeStatNum(albumStatVo.getPlayStatNum());
			albumInfoIndex.setBuyStatNum(albumStatVo.getBuyStatNum());
			albumInfoIndex.setCommentStatNum(albumStatVo.getCommentStatNum());
			// 设置热门排名的方法
			albumInfoIndex.setHotScoreCalculate();
		});
		// 阻塞,cf1-cf4全部完成继续往下执行
		CompletableFuture.allOf(cf1, cf2, cf5, cf3, cf4).join();
		log.info("对象已封装好,即将进入保存 :: {}", albumInfoIndex);
		albumIndexRepository.save(albumInfoIndex);
		// 初始化补全的专辑信息索引库
		CompletableFuture.runAsync(() -> initializeSuggestIndex(albumInfoIndex));
		// 将新专辑id维护进布隆过滤器
		RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
		bloomFilter.add(albumId);
	}

	/**
	 * 初始化补全的专辑信息索引库
	 *
	 * @param albumInfoIndex 专辑信息索引
	 */
	private void initializeSuggestIndex(AlbumInfoIndex albumInfoIndex) {
		// 	标题
		SuggestIndex albumTitleSuggestIndex = new SuggestIndex();
		String albumTitle = albumInfoIndex.getAlbumTitle();
		albumTitleSuggestIndex.setId(UUID.randomUUID().toString().replaceAll("-", ""));
		albumTitleSuggestIndex.setTitle(albumTitle);
		albumTitleSuggestIndex.setKeyword(new Completion(new String[]{albumTitle}));
		albumTitleSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumTitle)}));
		albumTitleSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumTitle)}));
		suggestIndexRepository.save(albumTitleSuggestIndex);
		// 	简介
		SuggestIndex albumIntroSuggestIndex = new SuggestIndex();
		String albumIntro = albumInfoIndex.getAlbumIntro();
		albumIntroSuggestIndex.setId(UUID.randomUUID().toString().replaceAll("-", ""));
		albumIntroSuggestIndex.setTitle(albumIntro);
		albumIntroSuggestIndex.setKeyword(new Completion(new String[]{albumIntro}));
		albumIntroSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumIntro)}));
		albumIntroSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumIntro)}));
		suggestIndexRepository.save(albumIntroSuggestIndex);
		// 	作者
		SuggestIndex announcerSuggestIndex = new SuggestIndex();
		String announcerName = albumInfoIndex.getAnnouncerName();
		announcerSuggestIndex.setId(UUID.randomUUID().toString().replaceAll("-", ""));
		announcerSuggestIndex.setTitle(announcerName);
		announcerSuggestIndex.setKeyword(new Completion(new String[]{announcerName}));
		announcerSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(announcerName)}));
		announcerSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(announcerName)}));
		suggestIndexRepository.save(announcerSuggestIndex);
	}

	/**
	 * 下架专辑
	 *
	 * @param albumId
	 */
	@Override
	public void lowerAlbum(Long albumId) {
		albumIndexRepository.deleteById(albumId);
	// 	1.创建新的布隆过滤器,
	// 	2.删除旧的布隆过滤器
	// 	3.新的改名为旧的
	// 		"{" .. album:bloom:filter .. "}:config"
	// 		album:bloom:filter
	// 	RedisConstant.ALBUM_BLOOM_FILTER
	// 	RedisConstant.ALBUM_BLOOM_FILTER_NEW
		RBloomFilter<Object> bloomFilterNew = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER_NEW);
		bloomFilterNew.tryInit(10000000,0.0001);
		albumIndexRepository.findAll().forEach(albumInfoIndex -> bloomFilterNew.add(albumInfoIndex.getId()));
		String script = "redis.call(\"del\" , KEYS[1])\n" +
				"redis.call(\"del\" , \"{\"..KEYS[1]..\"}:\"..\"config\")\n" +
				"redis.call(\"rename\" , KEYS[2] , KEYS[1])\n" +
				"redis.call(\"rename\" , \"{\"..KEYS[2]..\"}:\"..\"config\" , \"{\"..KEYS[1]..\"}:\"..\"config\")\n" +
				"return 1" ;
		Long executeResult = redisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList(RedisConstant.ALBUM_BLOOM_FILTER, RedisConstant.ALBUM_BLOOM_FILTER_NEW));
		if(executeResult.equals(1L)){
			log.info("布隆过滤器重建成功........");
		}else{
			log.info("布隆过滤器重建失败........");
		}
	}

	/**
	 * 根据参数获取专辑列表
	 *
	 * @param albumIndexQuery 参数
	 * @return 专辑列表等等信息的封装类
	 */
	@SneakyThrows
	@Override
	public AlbumSearchResponseVo findAlbumList(AlbumIndexQuery albumIndexQuery) {
		SearchRequest searchRequest = this.buildSearchRequest(albumIndexQuery);
		log.info("searchRequest: {}",searchRequest);
		SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
		return this.parseSearchRequest(searchResponse, albumIndexQuery);
	}

	/**
	 * 解析响应对象返回专辑列表等一系列信息
	 *
	 * @param searchResponse  搜索响应对象
	 * @param albumIndexQuery 请求参数
	 * @return 专辑列表等一系列信息
	 */
	private AlbumSearchResponseVo parseSearchRequest(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {
		AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
		// 总记录数
		HitsMetadata<AlbumInfoIndex> hitsMetadata = searchResponse.hits();
		Long total = Optional.ofNullable(hitsMetadata.total().value()).orElse(0L);
		albumSearchResponseVo.setTotal(total);
		// 每页显示条数
		albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
		// 第几页
		albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
		// 总页数
		albumSearchResponseVo.setTotalPages((long) PageUtil.totalPage(total.intValue(), albumIndexQuery.getPageSize()));
		// 商品数据
		albumSearchResponseVo.setList(hitsMetadata
				.hits()
				.stream()
				.filter(albumInfoIndexHit -> Objects.nonNull(albumInfoIndexHit.source()))
				.map(albumInfoIndexHit -> {
					AlbumInfoIndex albumInfoIndex = albumInfoIndexHit.source();
					Map<String, List<String>> highlight = albumInfoIndexHit.highlight();
					AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
					BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
					if (!CollectionUtils.isEmpty(highlight)) {
						List<String> albumTitle = highlight.get("albumTitle");
						if (!CollectionUtils.isEmpty(albumTitle))
							albumInfoIndexVo.setAlbumTitle(albumTitle.get(0));
						// TODO 专辑简介部分如果添加标签高亮显示的话,前端页面是不渲染的
						// List<String> albumIntro = highlight.get("albumIntro");
						// if(!CollectionUtils.isEmpty(albumIntro)){
						// 	albumInfoIndexVo.setAlbumIntro(albumIntro.get(0));
						// }
					}
					return albumInfoIndexVo;
				}).collect(Collectors.toList()));
		log.info("albumSearchResponseVo: {}", albumSearchResponseVo);
		return albumSearchResponseVo;
	}

	/**
	 * 搜索专辑列表的搜索请求对象的封装
	 *
	 * @param albumIndexQuery 请求参数
	 * @return 返回搜索对象
	 */
	private SearchRequest buildSearchRequest(AlbumIndexQuery albumIndexQuery) {
		BoolQuery.Builder bool = QueryBuilders.bool();
		// 三级分类,如果存在就拼接
		if (albumIndexQuery.getCategory1Id() != null)
			bool.must(mustQueryBuilder -> mustQueryBuilder.term(termQuery -> termQuery.field("category1Id").value(albumIndexQuery.getCategory1Id())));
		if (albumIndexQuery.getCategory2Id() != null)
			bool.must(mustQueryBuilder -> mustQueryBuilder.term(termQuery -> termQuery.field("category2Id").value(albumIndexQuery.getCategory2Id())));
		if (albumIndexQuery.getCategory3Id() != null)
			bool.must(mustQueryBuilder -> mustQueryBuilder.term(termQuery -> termQuery.field("category3Id").value(albumIndexQuery.getCategory3Id())));
		// 属性ID 和 属性值ID
		List<String> attributeList = albumIndexQuery.getAttributeList();
		if (!CollectionUtils.isEmpty(attributeList)) {
			attributeList.forEach(attribute -> {
				String[] split = attribute.split(":");
				if (!StringUtils.isEmpty(attribute) && split.length == 2) {
					bool.must(mustQueryBuilder -> mustQueryBuilder
							.nested(nestQueryBuilder -> nestQueryBuilder
									.path("attributeValueIndexList")
									.query(QueryBuilders
											.bool()
											.must(q -> q.term(t -> t.field("attributeValueIndexList.attributeId").value(split[0])))
											.must(q -> q.term(t -> t.field("attributeValueIndexList.valueId").value(split[1])))
											.build()
											._toQuery())));
				}
			});
		}
		// 排序规则
		final FieldSort fieldSort;
		if (!StringUtils.isEmpty(albumIndexQuery.getOrder())) {
			String[] sortList = albumIndexQuery.getOrder().split(":");
			if (sortList.length != 2)
				throw new IllegalArgumentException("排序字段不规范");
			SortOrder sortOrder = sortList[1].equalsIgnoreCase("desc") ? SortOrder.Desc : SortOrder.Asc;
			fieldSort = this.getFieldSort(sortList[0], sortOrder);
		} else {
			fieldSort = this.getFieldSort("1", SortOrder.Asc);
		}
		// 如果存在关键字,就拼接条件
		if (!StringUtils.isEmpty(albumIndexQuery.getKeyword())) {
			MultiMatchQuery multiMatchQuery = new MultiMatchQuery.Builder().fields("albumTitle", "albumIntro").query(albumIndexQuery.getKeyword()).build();
			bool.must(mustQueryBuilder -> mustQueryBuilder.multiMatch(multiMatchQuery));
		}
		SearchRequest.Builder builder = new SearchRequest
				.Builder()
				.index("albuminfo")
				.query(bool.build()._toQuery())
				.sort(sort -> sort.field(fieldSort))
				.from((albumIndexQuery.getPageNo() - 1) * albumIndexQuery.getPageSize())
				.size(albumIndexQuery.getPageSize());
		if (!StringUtils.isEmpty(albumIndexQuery.getKeyword())) {
			// 高亮显示Map
			Map<String, HighlightField> highlightFieldMap = new HashMap<>();
			highlightFieldMap.put("albumTitle", new HighlightField.Builder().preTags("<font color='red'>").postTags("</font>").build());
			highlightFieldMap.put("albumIntro", new HighlightField.Builder().preTags("<font color='red'>").postTags("</font>").build());
			builder.highlight(high -> high.fields(highlightFieldMap));
		}
		return builder.build();
	}

	/**
	 * 根据分类ID查询专辑信息索引搜索视图。
	 *
	 * @param category1Id 分类一级ID，用于筛选相册信息。
	 * @return 相册信息索引搜索视图列表，包含每个分类下的热门相册。
	 */
	@SneakyThrows
	@Override
	public List<AlbumInfoIndexSearchVo> channel(Long category1Id) {
		// 通过远程调用获取指定分类一级ID下的所有分类三级信息
		Map<Long, BaseCategory3> baseCategory3Map = categoryFeignClient.findTopBaseCategory3ByC1Id(category1Id)
				.getData()
				.stream()
				.filter(Objects::nonNull)
				.collect(Collectors.toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));
		// 构建相册分类三级ID的列表，用于后续的ES查询条件
		List<FieldValue> fieldValueList = baseCategory3Map.keySet().stream().map(id -> new FieldValue.Builder().longValue(id).build()).collect(Collectors.toList());
		// 构造ES的搜索请求，包括查询条件和聚合操作
		// 获取 es 数据 AlbumInfoIndex
		SearchRequest searchRequest = new SearchRequest
				.Builder()
				.index("albuminfo")
				.query(QueryBuilders
						.bool()
						.must(QueryBuilders
								.term()
								.field("category1Id")
								.value(category1Id)
								.build()
								._toQuery())
						.must(QueryBuilders
								.terms()
								.field("category3Id")
								.terms(tqf -> tqf.value(fieldValueList))
								.build()
								._toQuery())
						.build()
						._toQuery())
				.source(new SourceConfig.Builder().fetch(false).build())
				.aggregations("category3IdAgg", category3IdAgg -> category3IdAgg
						.terms(category3IdAggTerms -> category3IdAggTerms
								.field("category3Id")
								.size(100))
						.aggregations("topAlbumInfoAgg", topAlbumInfoAgg -> topAlbumInfoAgg
								.topHits(builder -> builder
										.size(6)
										.sort(sort -> sort
												.field(fileSort -> fileSort
														.field("hotScore")
														.order(SortOrder.Asc))))))
				.build();
		// 执行ES搜索请求，获取搜索响应
		SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
		// 从搜索响应中提取分类三级ID的聚合结果
		LongTermsAggregate category3IdAgg = (LongTermsAggregate) searchResponse.aggregations().get("category3IdAgg")._get();
		Buckets<LongTermsBucket> buckets = category3IdAgg.buckets();
		List<LongTermsBucket> array = buckets.array();
		// 遍历聚合结果，构建相册信息搜索视图列表
		List<AlbumInfoIndexSearchVo> albumInfoIndexSearchVos = new ArrayList<>();
		for (LongTermsBucket longTermsBucket : array) {
			AlbumInfoIndexSearchVo albumInfoIndexSearchVo = new AlbumInfoIndexSearchVo();
			// 设置分类三级信息
			albumInfoIndexSearchVo.setBaseCategory3(baseCategory3Map.get(longTermsBucket.key()));
			// 设置热门相册列表
			Aggregate topAlbumInfoAgg = longTermsBucket.aggregations().get("topAlbumInfoAgg");
			albumInfoIndexSearchVo.setList(topAlbumInfoAgg
					.topHits()
					.hits()
					.hits()
					.stream()
					.map(Hit::source)
					.filter(Objects::nonNull)
					.map(jsonData -> jsonData.to(AlbumInfoIndex.class))
					.collect(Collectors.toList()));
			albumInfoIndexSearchVos.add(albumInfoIndexSearchVo);
		}
		return albumInfoIndexSearchVos;
	}

	@SneakyThrows
	@Override
	public List<String> completeSuggest(String keyword) {
		SearchRequest searchRequest = this.buildCompleteSuggestSearchRequest(keyword);
		SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(searchRequest, SuggestIndex.class);
		List<String> suggestionKeyword = this.parseCompleteSuggestSearchResponse(searchResponse, "suggestionKeyword");
		List<String> suggestionkeywordPinyin = this.parseCompleteSuggestSearchResponse(searchResponse, "suggestionkeywordPinyin");
		List<String> suggestionkeywordSequence = this.parseCompleteSuggestSearchResponse(searchResponse, "suggestionkeywordSequence");
		return Stream.of(suggestionKeyword, suggestionkeywordPinyin, suggestionkeywordSequence)
				.flatMap(List::stream)
				.collect(Collectors.toList());
	}

	@SneakyThrows
	@Override
	public void updateLatelyAlbumRanking() {
		Aggregation.Builder.ContainerBuilder aggregationBuilder = new Aggregation.Builder()
				.terms(terms -> terms.field("category1Id").size(100));
		// 定义统计维度
		String[] rankingDimensionArray = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
		for (String ranking : rankingDimensionArray) {
			aggregationBuilder.aggregations(ranking + "Agg", subAggBuilder -> subAggBuilder
					.topHits(topHit -> topHit.size(10).sort(sort -> sort.field(fieldSort -> fieldSort.field(ranking).order(SortOrder.Desc)))));
		}
		SearchRequest searchRequest = new SearchRequest.Builder()
				.index("albuminfo")
				.query(QueryBuilders.matchAll().build()._toQuery())
				.source(builder -> builder.fetch(false))
				.aggregations("category1IdAgg", aggregationBuilder.build())
				.build();
		SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
		LongTermsAggregate category1IdAgg = (LongTermsAggregate) searchResponse.aggregations().get("category1IdAgg")._get();
		List<LongTermsBucket> array = category1IdAgg.buckets().array();
		for (LongTermsBucket longTermsBucket : array) {
			long category1Id = longTermsBucket.key();
			String rankingRedisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
			for (String ranking : rankingDimensionArray) {
				Aggregate aggregate = longTermsBucket.aggregations().get(ranking + "Agg");
				TopHitsAggregate topHitsAggregate = (TopHitsAggregate) aggregate._get();
				List<AlbumInfoIndex> albumInfoIndexList = topHitsAggregate
						.hits()
						.hits()
						.stream()
						.map(Hit::source)
						.filter(Objects::nonNull)
						.map(jsonData -> jsonData.to(AlbumInfoIndex.class))
						.collect(Collectors.toList());
				redisTemplate.opsForHash().put(rankingRedisKey, ranking, JSON.toJSONString(albumInfoIndexList));
			}
		}
	}

	@Override
	public List<AlbumInfoIndexVo> findRankingList(Long category1Id, String dimension) {
		String rankingRedisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
		String json = (String) redisTemplate.opsForHash().get(rankingRedisKey, dimension);
		List<AlbumInfoIndex> albumInfoIndexList = JSON.parseArray(json, AlbumInfoIndex.class);
		return StringUtils.isEmpty(json) ? List.of() : albumInfoIndexList
				.stream()
				.map(albumInfoIndex -> {
					AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
					BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
					return albumInfoIndexVo;
				}).collect(Collectors.toList());
	}

	/**
	 * 自动补全功能解析搜索响应
	 *
	 * @param searchResponse 搜索响应
	 * @param keyword        集合数据的key
	 * @return 自动补全的字符串集合
	 */
	private List<String> parseCompleteSuggestSearchResponse(SearchResponse<SuggestIndex> searchResponse, String keyword) {
		return searchResponse
				.suggest()
				.get(keyword)
				.get(0)
				.completion()
				.options()
				.stream()
				.map(CompletionSuggestOption::source)
				.filter(Objects::nonNull)
				.map(SuggestIndex::getTitle)
				.collect(Collectors.toList());
	}


	/**
	 * 创建关键字补全的搜索请求
	 *
	 * @param keyword 关键字
	 * @return 搜索请求
	 */
	private SearchRequest buildCompleteSuggestSearchRequest(String keyword) {
		SearchRequest searchRequest = new SearchRequest
				.Builder()
				.suggest(suggest -> suggest
						.suggesters("suggestionKeyword", builder -> builder
								.prefix(keyword)
								.completion(compBuilder -> compBuilder
										.field("keyword")
										.skipDuplicates(true)
										.size(10)
										.fuzzy(fuzzyBuilder -> fuzzyBuilder
												.fuzziness("auto"))))
						.suggesters("suggestionkeywordPinyin", builder -> builder
								.prefix(keyword)
								.completion(compBuilder -> compBuilder
										.field("keywordPinyin")
										.skipDuplicates(true)
										.size(10)
										.fuzzy(fuzzyBuilder -> fuzzyBuilder
												.fuzziness("auto"))))
						.suggesters("suggestionkeywordSequence", builder -> builder
								.prefix(keyword)
								.completion(compBuilder -> compBuilder
										.field("keywordSequence")
										.skipDuplicates(true)
										.size(10)
										.fuzzy(fuzzyBuilder -> fuzzyBuilder
												.fuzziness("auto"))))
				)
				.build();
		return searchRequest;
	}

	/**
	 * 返回排序字段
	 *
	 * @param sortList  切分好的数组
	 * @param sortOrder 升序/降序
	 * @return FieldSort
	 */
	private FieldSort getFieldSort(String sortList, SortOrder sortOrder) {
		return switch (sortList) {
			case "1" -> new FieldSort.Builder().field("hotScore").order(sortOrder).build();
			case "2" -> new FieldSort.Builder().field("playStatNum").order(sortOrder).build();
			case "3" -> new FieldSort.Builder().field("createTime").order(sortOrder).build();
			default -> throw new GuiguException(ResultCodeEnum.ORDER_FIELD_NOT_EXISTS);
		};
	}

	/**
	 * 上架专辑线程池版本
	 */
	@SneakyThrows
	public void upperAlbumThreadPoolExecutor(Long albumId) {
		Long start = System.currentTimeMillis();
		//  线程池版本
		// 	将展示的数据和参与检索的数据放入 ES
		// 	查询 专辑 的基础数据
		Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
		AlbumInfo albumInfo = albumInfoResult.getData();
		AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
		BeanUtils.copyProperties(albumInfo, albumInfoIndex);
		albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));
		// 创建线程池
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8,
				16,
				5,
				TimeUnit.MINUTES,
				new ArrayBlockingQueue<>(3),
				Executors.defaultThreadFactory(),
				new ThreadPoolExecutor.AbortPolicy());
		// 创建 倒计数器
		CountDownLatch countDownLatch = new CountDownLatch(3);
		threadPoolExecutor.submit(() -> {
			// 属性值
			List<AttributeValueIndex> attributeValueIndexList = albumInfo
					.getAlbumAttributeValueVoList()
					.stream()
					// 将 albumAttributeValue 对象
					.map(albumAttributeValue -> AttributeValueIndex
							.builder()
							.attributeId(albumAttributeValue.getAttributeId())
							.valueId(albumAttributeValue.getValueId())
							.build())
					.collect(Collectors.toList());
			albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
			countDownLatch.countDown();
		});
		threadPoolExecutor.submit(() -> {
			// 分类
			Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.findBaseCategoryView(albumId);
			BaseCategoryView baseCategoryView = baseCategoryViewResult.getData();
			albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
			albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
			albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
			countDownLatch.countDown();
		});

		threadPoolExecutor.submit(() -> {
			// 统计数据
			Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.getAlbumStatByAlbumId(albumId);
			AlbumStatVo albumStatVo = albumStatVoResult.getData();
			albumInfoIndex.setPlayStatNum(albumStatVo.getPlayStatNum());
			albumInfoIndex.setSubscribeStatNum(albumStatVo.getPlayStatNum());
			albumInfoIndex.setBuyStatNum(albumStatVo.getBuyStatNum());
			albumInfoIndex.setCommentStatNum(albumStatVo.getCommentStatNum());
			// 设置热门排名的方法
			albumInfoIndex.setHotScoreCalculate();
			countDownLatch.countDown();
		});
		countDownLatch.await();
		log.info("对象已封装好,即将进入保存 :: {}", albumInfoIndex);
		albumIndexRepository.save(albumInfoIndex);
		Long end = System.currentTimeMillis();
		log.info("SearchServiceImpl.upperAlbum 方法耗时 : {}", (end - start));
	}


	/**
	 * 上架专辑的原始版本
	 */
	public void upperAlbumOrigin(Long albumId) {
		// 	将展示的数据和参与检索的数据放入 ES
		// 	查询 专辑 的基础数据
		Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
		AlbumInfo albumInfo = albumInfoResult.getData();
		AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
		BeanUtils.copyProperties(albumInfo, albumInfoIndex);
		albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));
		// 属性值
		List<AttributeValueIndex> attributeValueIndexList = albumInfo
				.getAlbumAttributeValueVoList()
				.stream()
				// 将 albumAttributeValue 对象
				.map(albumAttributeValue -> AttributeValueIndex
						.builder()
						.attributeId(albumAttributeValue.getAttributeId())
						.valueId(albumAttributeValue.getValueId())
						.build())
				.collect(Collectors.toList());
		albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
		// 分类
		Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.findBaseCategoryView(albumId);
		BaseCategoryView baseCategoryView = baseCategoryViewResult.getData();
		albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
		albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
		albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
		// 统计数据
		Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.getAlbumStatByAlbumId(albumId);
		AlbumStatVo albumStatVo = albumStatVoResult.getData();
		albumInfoIndex.setPlayStatNum(albumStatVo.getPlayStatNum());
		albumInfoIndex.setSubscribeStatNum(albumStatVo.getPlayStatNum());
		albumInfoIndex.setBuyStatNum(albumStatVo.getBuyStatNum());
		albumInfoIndex.setCommentStatNum(albumStatVo.getCommentStatNum());
		// 设置热门排名的方法
		albumInfoIndex.setHotScoreCalculate();
		albumIndexRepository.save(albumInfoIndex);
	}
}
